package com.java.concurrent;

/**
 * @program: java_basic_knowledge
 * @description:
 * @author: CaoYong
 * @create: 2021-04-22 10:50
 **/

public class SynchronizedKnowledge {

    public static class Counter {

        private int count;

        public void incr() {
            count ++;
        }

        // synchronized使得同时只能有一个线程执行实例方法，但是如果线程是访问的不同对象则可以同时执行synchronized修饰的实例方法
        // synchronized时间保护的是当前的实例对象，即this,this对象有一个锁和一个等待队列，锁只能被一个线程持有，其他试图获得锁的线程需要等待
        // synchronized实例方法的执行过程
        // 1. 尝试获得锁，如果能获得锁则进行下一步否则加入等待队列，阻塞并等待召唤
        // 2. 执行实例方法
        // 3. 释放锁，如果等待队列上有等待的程序，则从中取一个唤醒，不保证公平
        // synchronized保护的是对象而不是代码，只要访问的是同一个对象的synchronized方法，即使是不同的代码也会同步顺序访问，但是并不能防止非synchronized方法被同时执行
        // 在保护变量时需要在所有访问该变量的方法上加synchronized
        public synchronized void syncIncr() {
            count ++;
        }

        // synchronized修饰代码块，（）里面是要保护的对象，在实例方法中即this,{}里面即要同步执行的代码块
        public void blockIncr() {
            synchronized (this) {
                count ++;
            }
        }

        public int getCount() {
            return count;
        }

        public synchronized int getSyncCount() {
            return count;
        }

    }

    public static class StaticCounter {

        private static int count = 0;

        // synchronized可以用于静态方法，保护的是类对象，每个对象都有一个锁和等待队列
        // synchronized静态方法和实例方法保护的是不同的对象，一个是类对象，一个是实例对象
        public static synchronized void incr() {
            count++;
        }

        // 静态方法要保护的对象即类对象
        public static void blockIncr() {
            synchronized (StaticCounter.class) {
                count ++;
            }
        }

        public static synchronized int getCount() {
            return count;
        }
    }

    public static class CounterThread extends Thread {
        Counter counter;

        StaticCounter staticCounter;

        public CounterThread(Counter counter) {
            this.counter = counter;
        }

        public CounterThread(StaticCounter staticCounter) {
            this.staticCounter = staticCounter;
        }

        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
//                counter.incr();
//                counter.syncIncr();
                StaticCounter.incr();
            }
        }
    }

    public static void testCounter() throws Exception{

        long start = System.currentTimeMillis();
        Thread[] threads = new Thread[1000];
        Counter counter = new Counter();

        for (int i = 0; i < 1000; i++) {
            threads[i] = new CounterThread(counter);
            threads[i].start();
        }

        for (int i = 0; i < 1000; i++) {
            threads[i].join();
        }

        System.out.println(counter.getCount());
        long end = System.currentTimeMillis();
        System.out.println("用时：" + (end - start));
    }

    public static void testStaticCount() throws Exception{
        Thread[] threads = new Thread[1000];
        StaticCounter staticCounter = new StaticCounter();

        for (int i = 0; i < 1000; i++) {
            threads[i] = new CounterThread(staticCounter);
            threads[i].start();
        }

        for (int i = 0; i < 1000; i++) {
            threads[i].join();
        }

        System.out.println(StaticCounter.count);
    }

    /**
     * 1. 可重入性
     * synchronized是可重入的，对同一个执行线程，在其获得了锁之后，在调用其他需要同样锁的代码时可以直接调用
     * 可重入是通过记录锁的持有线程和持有数量来实现的，当执行synchronized方法时，检查对象是否已经被锁，是则检查是否被当前线程锁定，如果是在增加锁持有数量，否则进入等待队列，释放锁时减少持有数量，为0时释放整个锁
     *
     * 2. 内存可见性
     * synchronized可以保证内存可见性，释放锁时所有写入都会写回内存，而获得锁后都会从内存中读最新数据
     *
     * 3.死锁
     * synchronized可能回导致死锁
     */
    public static void howToUnderstandSynchronized() {

    }

    public static void main(String[] args) throws Exception{
//        testCounter();
        testStaticCount();
    }
}
